package com.gonsin.project.creator.create;

import com.gonsin.project.creator.utils.*;
import com.google.gson.Gson;
import com.google.gson.reflect.TypeToken;
import freemarker.ext.beans.BeansWrapperBuilder;
import freemarker.template.Configuration;
import freemarker.template.TemplateExceptionHandler;
import jdk.nashorn.api.scripting.ScriptObjectMirror;
import org.apache.commons.io.FileUtils;

import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Map;

/**
 * java script上的内置方法
 */
public class JavaScriptBinder implements IJavaScriptBinder {

    private File templatePath;

    private File inputJs;
    private File lastFile;
    private File remarkFile;

    private boolean ignorePackageName;      // 忽略包名

    private boolean force;                  // 是否强制覆盖原来文件

    public JavaScriptBinder(File templatePath, boolean ignorePackageName, boolean force) {
        this.templatePath = templatePath;
        this.inputJs = new File(templatePath.getAbsolutePath() + File.separator + "input.js");
        this.lastFile = new File(templatePath.getAbsolutePath() + File.separator + ".last");
        this.remarkFile = new File(templatePath.getAbsolutePath() + File.separator + ".remark");
        this.ignorePackageName = ignorePackageName;
        this.force = force;
    }

    @Override
    public Object input(String message, Object defaultValue) {
        if(defaultValue == null){
            Object in = CmdUtils.readCmd(message, false);
            if(in instanceof String){
                return ((String) in).trim();
            }
            return in;
        } else {
            Object in = CmdUtils.readCmd(message, true);
            if(in == null){
                return defaultValue;
            }
//            System.out.println("in = " + in + ", in.class = " + in.getClass());
            if(in instanceof String && "".equalsIgnoreCase((String) in)){
                return defaultValue;
            }
            if(in instanceof String){
                return ((String) in).trim();
            }
            return in;
        }
    }

    @Override
    public void output(Object message) {
        if(message instanceof ScriptObjectMirror){
            System.out.println(JsUtils.toObject((ScriptObjectMirror) message));
            return;
        }
        System.out.println(message);
    }

    @Override
    public String format(String format, Long time) {
        return new SimpleDateFormat(format).format(time);
    }

    @Override
    public Long time() {
        return System.currentTimeMillis();
    }

    @Override
    public String camelToUnderline(String message) {
        return CharUtils.camelToUnderline(message);
    }

    @Override
    public String underlineToCamel(String message) {
        return CharUtils.underlineToCamel(message, true);
    }

    @Override
    public String firstLowercase(String message) {
        return CharUtils.firstLowercase(message);
    }

    @Override
    public String firstUppercase(String message) {
        return CharUtils.firstUppercase(message);
    }

    @Override
    public String javaType2dbType(String type) {
        return TypeUtils.getDbType(type);
    }

    @Override
    public String javaType2objectType(String type) {
        return TypeUtils.getObjectType(type);
    }

    @Override
    public String dbColumn(String name) {
        return TypeUtils.dbColumn(name);
    }

    @Override
    public void go(Object in) throws Exception {

        Object o = null;

        if(in instanceof ScriptObjectMirror){
            o = JsUtils.toObject((ScriptObjectMirror) in);
        } else {
            o = in;
        }
        // 初始化freemarker
        Configuration cfg = new Configuration(Configuration.VERSION_2_3_22);
        cfg.setObjectWrapper(new BeansWrapperBuilder(Configuration.VERSION_2_3_22).build());
        try {
            cfg.setDirectoryForTemplateLoading(templatePath);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        cfg.setDefaultEncoding("UTF-8");
        cfg.setTemplateExceptionHandler(TemplateExceptionHandler.RETHROW_HANDLER);


        File newPath = new File("");
        File originPath = templatePath;
        File[] originFiles = originPath.listFiles();

        for(File origin : originFiles){
            if(origin.getAbsolutePath().equals(inputJs.getAbsolutePath())){
                continue;
            }
            if(origin.getAbsolutePath().equals(lastFile.getAbsolutePath())){
                continue;
            }
            if(origin.getAbsolutePath().equals(remarkFile.getAbsolutePath())){
                continue;
            }
            if(".keep".equalsIgnoreCase(origin.getName())){
                continue;
            }
            if(origin.isDirectory()){
                File newChildPath = mkdir(cfg, origin, newPath, o);
                generalDir(cfg, origin, newChildPath, o);
            } else if(origin.isFile()){
                String newFilename = FreemarkerUtils.formatByContent(cfg, origin.getName(), o);
                File newFile = new File(newPath + File.separator + newFilename);

                handleNewFile(cfg, o, newFile, origin);

            } else {
                continue;
            }
        }

    }

    @Override
    public void save(Object so) throws Exception {
        Object o = null;
        if(so instanceof ScriptObjectMirror){
            o = JsUtils.toObject((ScriptObjectMirror) so);
        } else {
            o = so;
        }
        String json = new Gson().toJson(o);
        try {
            FileUtils.writeStringToFile(lastFile, json, "UTF8");
        } catch (IOException e) {
            e.printStackTrace();
//            throw new RuntimeException(e);
        }
    }

    @Override
    public String reload(Object defaultValue) throws Exception {
        try {
            if(lastFile.exists()){
                String json = FileUtils.readFileToString(lastFile, "UTF8");
//                Object obj = new Gson().fromJson(json, new TypeToken<Map<String, Object>>(){}.getRawType());
//
//                return JsUtils.toJsObject(obj);
                return json;
            }
        } catch (IOException e) {
            if(lastFile != null){
                lastFile.delete();
            }
        }
        if(defaultValue == null){
            return null;
        }
        if(defaultValue instanceof ScriptObjectMirror){
            ScriptObjectMirror som = (ScriptObjectMirror)defaultValue;
            String json = new Gson().toJson(JsUtils.toObject((ScriptObjectMirror) defaultValue));
            return json;
        }

        return null;
    }

    private void generalDir(Configuration cfg, File originPath, File newPath, Object o) throws Exception {
        File[] originFiles = originPath.listFiles();

        for(File origin : originFiles){
            if(origin.equals(inputJs)){
                continue;
            }
            if(origin.getAbsolutePath().equals(lastFile.getAbsolutePath())){
                continue;
            }
            if(origin.getAbsolutePath().equals(remarkFile.getAbsolutePath())){
                continue;
            }
            if(".keep".equalsIgnoreCase(origin.getName())){
                continue;
            }
            if(origin.isDirectory()){
                File newChildPath = mkdir(cfg, origin, newPath, o);
                generalDir(cfg, origin, newChildPath, o);
            } else if(origin.isFile()){
                String originFilename = origin.getName().replace("#{", "${");
                String newFilename = FreemarkerUtils.formatByContent(cfg, originFilename, o);
                File newFile = new File(newPath + File.separator + newFilename);
                if(!newFile.exists() || force){
                    handleNewFile(cfg, o, newFile, origin);
                }

            } else {
                continue;
            }
        }
    }


    private void handleNewFile(Configuration cfg, Object o, File newFile, File originFile) throws IOException {

        if(newFile.exists() && !force){
            return;
        }
        if(newFile.exists()){
            newFile.delete();
        }
        newFile.createNewFile();
        String templateName = PathUtils.relativePath(originFile, templatePath);
        String content = FreemarkerUtils.format(cfg, templateName, o);
        FileUtils.writeStringToFile(newFile, content);
    }


    /**
     * 创建目录
     * @param origin
     * @return
     */
    private File mkdir(Configuration cfg, File origin, File parentPath, Object o) throws Exception {

        String originPathName = origin.getName().replace("#{", "${");
        String newPathName = FreemarkerUtils.formatByContent(cfg, originPathName, o);
        String newPathReal = newPathName;
        if(!ignorePackageName){
            newPathReal = newPathName.replaceAll("\\.", "/");
            if(SystemUtils.isWindows()){
                newPathReal = newPathReal.replaceAll("/", "\\\\");
            }
        }
        newPathReal = parentPath.getAbsolutePath() + File.separator + newPathReal;
        File newPathFile = new File(newPathReal);
        if(!newPathFile.exists()){
            newPathFile.mkdirs();
        }
        return newPathFile;
    }
}
